#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <linux/limits.h>
#include <signal.h>

int runbin;//执行子进程id
int loopflag;//死循环标志位 0为无死循环


int judgeDir(char  *argv);//判断是否为目录 为目录返回1 否则程序退出
int openfileRD(char *filename);//只读方式打开某一文件，返回其文件描述符
void adddirargv(char *beforepath,char *afterpath);//对目录进行添加/操作
void createonDir(char *path,char *dirname,char *returndir);//在给定的目录下创建目录 returndir带有/
void writeSome(char *filename,char * buf);//写文件函数
void gccProcess(char *plugdir,char *delwithfilename,char *gccfilename,char *gccofilename);//gcc进程
void runProcess(char *plugdir,char *runfilepath,char **exec);//run进程
void passcorrect(char *iniDir,char *filename);//答案正确
void passfail(char *iniDir,char *filename);//答案错误
void delwithloop(char *iniDir,char *filename);//死循环处理函数
void gccfail(char *iniDir,char *filename);//编译失败处理函数
void initsetini(char **mainargv,/*char *swrunargv int *fd,*/char *rresult,char *swrunargvpath);
void stopLoop(int sig);//死循环处理函数
void extractArgv(char *p,char **execargv);//提取[]分割的参数 p为原始数据，exeargv为提取后保存的数据
void writeResult(char *filename,char *writesome );//写内容 追加方式
void showinf(char *dir,char *file1,char *file2);
void cpfailf(char *cprdir,char *cpddir,char *filename);
void rmcachedir(char * dir);

void main(int argc, char  *argv[])
{
	if(argc!=2)
	{
		printf("用法：Bin <要判断的文件目录>\n");
		exit(-1);
	}
	int isDir;
	isDir=judgeDir(argv[1]);
	if(isDir==1)
	{
		DIR *dp;
		struct dirent *entp;
		if((dp=opendir(argv[1]))==NULL)
		{
			perror("目录打开失败!");
			exit(-1);
		}		
		char DirPath[2048]={'\0'};
		adddirargv(argv[1],DirPath);		
		char ComplieDirPath[2048]={'\0'};
		char iniDirPath[2048]={'\0'};
		char errorf[2048]={'\0'};
		//创建编译二进制文件临时存放目录
		createonDir(DirPath,"BinCache",ComplieDirPath);
		//创建配置文件保存目录
		createonDir(DirPath,"Plug",iniDirPath);
		createonDir(iniDirPath,"fail",errorf);
		//定义需求参数
		char correctResult[1024]={'\0'};//正确的结果
		char *execvpargv[1024]={'\0'};//执行主函数有参数的变量
		initsetini(execvpargv,correctResult,iniDirPath);
		int i;

		//循环判断目录中的.c文件
		while((entp=readdir(dp))!=NULL)
		{
			if(strstr(entp->d_name,".c")==NULL)//忽略非.c文件
			{
				continue;
			}

			//重命名gcc编译后的二进制文件
			char complieOfname[1024]={'\0'};
			strncat(complieOfname,entp->d_name,strlen(entp->d_name)-2);

			//对gcc编译后的二进制文件增加全路径
			char allcomplieOfnamePath[2048]={'\0'};
			strncat(allcomplieOfnamePath,ComplieDirPath,strlen(ComplieDirPath));
			strncat(allcomplieOfnamePath,complieOfname,strlen(complieOfname));

			//取得带全路径的文件名
			char allPathFileName[2048]={'\0'};
			strncpy(allPathFileName,DirPath,strlen(DirPath));
			strncat(allPathFileName,entp->d_name,strlen(entp->d_name));

			//显示当前处理的进度
			printf("--->当前处理的文件为：%s\r",entp->d_name);
			fflush(stdout);

			//fork子进程进行gcc编译操作
			int compliePid;
			compliePid=fork();

			if(compliePid==0)//gcc编译进程
			{
				gccProcess(iniDirPath,entp->d_name,allPathFileName,allcomplieOfnamePath);
			}

			//wait操作
			int complieStatus;
			wait(&complieStatus);

			//判断编译是否通过
			if(WEXITSTATUS(complieStatus)>0)//编译没通过
			{
				gccfail(iniDirPath,entp->d_name);
				cpfailf(DirPath,errorf,entp->d_name);
				continue;

			}
			else if(WEXITSTATUS(complieStatus)==0)//编译通过
			{
				//创建管道用于接收程序的运行结果
				int resultfd[2];
				if(pipe(resultfd)<0)
				{
					perror("创建管道用于接收程序的运行结果失败！");
				}
				

				//fork子进程用于执行编译好的程序
				runbin=fork();
				if(runbin==0)//子进程用于运行
				{
					close(resultfd[0]);
					dup2(resultfd[1],1);	
					int infd;
					char temp[2048]={'\0'};
					strncpy(temp,iniDirPath,strlen(iniDirPath));
					strncat(temp,".runsw.ini",strlen(".runsw.ini"));
					infd=open(temp,O_RDONLY);
					dup2(infd,0);
					runProcess(iniDirPath,allcomplieOfnamePath,execvpargv);
				}

				alarm(7);
				signal(SIGALRM,stopLoop);

				//父进程的wait操作
				wait(NULL);

				if(loopflag==1)//死循环处理
				{
					delwithloop(iniDirPath,entp->d_name);  
					cpfailf(DirPath,errorf,entp->d_name);    
				}
				else if(loopflag==0)//非死循环
				{
					//获取子进程输出数据
					close(resultfd[1]);
					char runbinresult[PIPE_BUF]={'\0'};
					int tempi=0;
					do
					{
						if((tempi=read(resultfd[0],runbinresult,PIPE_BUF))==-1)
							break;
					}while(tempi>0);

					if(memcmp(runbinresult,correctResult,strlen(correctResult))==0)//结果匹配
					{
						//printf("=|=%s\n",runbinresult );
						passcorrect(iniDirPath,entp->d_name);
					}
					else if(memcmp(runbinresult,correctResult,strlen(correctResult))!=0)//结果不匹配
					{
						// int fd;
						// fd=open("/home/wanghao/eeee/t.log",O_RDWR|O_CREAT|O_APPEND,0666);
						// write();
						//printf("==%s\n",runbinresult );
						passfail(iniDirPath,entp->d_name);
						cpfailf(DirPath,errorf,entp->d_name);
					}
				}
			}		
		}
		rmcachedir(ComplieDirPath);//删除缓存目录
		printf("\n");
		showinf(iniDirPath,"result.log","compileError.log");//显示结果	
	}
}//<-main函数

int judgeDir(char  *argv)//判断是否为目录
{
	struct stat dirbuf;//stat结构体用于判断目录

	if(stat(argv,&dirbuf)!=0)
	{
		printf("该参数不是一个目录或者文件:(\n");
		exit(0);
	}

	if(S_ISDIR(dirbuf.st_mode))//目录
	{
		return 1;
	}
	else if(S_ISREG(dirbuf.st_mode))
	{
		if(strstr(argv,".c")==NULL)
		{
			printf("当前程序版本只能评测C源文件：）\n");
			exit(0);
		}
		else
		{
			printf("单个C源文件请手动编译，谢谢合作：）\n");
			exit(0);
		}
	}
}
void stopLoop(int sig)
{
	if(sig==SIGALRM)
	{
		loopflag=1;
		
		kill(runbin,SIGTERM);//杀死死循环进程

	}

}
void extractArgv(char *p,char **execargv)//处理函数
{
	int loopnum=0;
	while(*p=='[')
	{
		execargv[loopnum++]=++p;
		while(*p++!=']');
			*(p-1)='\0';
		
	}

}

void initsetini(char **mainargv,char *rresult,char *swrunargvpath)
{
	printf("请输入程序运行的main函数参数，以[]分割；若为空请直接回车:\n");
	char tempmainargv[512]={'\0'};
	fgets(tempmainargv,512,stdin);
	
	if(strlen(tempmainargv)==1||strlen(tempmainargv)==0)//空参数
	{


	}
	else
	{
		tempmainargv[strlen(tempmainargv)-1]='\0';//去掉回车

		char tempmainargvCache[820]={'\0'};
		strncat(tempmainargvCache,"[temp]",strlen("[temp]"));
		strncat(tempmainargvCache,tempmainargv,strlen(tempmainargv));
		extractArgv(tempmainargvCache,mainargv);
	}

	printf("请输入程序运行时需要输入的参数；若为空请直接回车:\n");
	char swrunargv[512]={'\0'};
	fgets(swrunargv,512,stdin);
	if(strlen(swrunargv)==1||strlen(swrunargv)==0)//空参数
	{

	}
	else
	{
		swrunargv[strlen(swrunargv)-1]='\0';
		char temp[2048]={'\0'};
		strncpy(temp,swrunargvpath,strlen(swrunargvpath));
		strncat(temp,".runsw.ini",strlen(".runsw.ini"));
		writeSome(temp,swrunargv);
	}

	printf("请输入正确的程序运行结果：\n" );
	fgets(rresult,512,stdin);
	if(strlen(rresult)==0||strlen(rresult)==1)//结果为空
	{

	}
	else
	{
		rresult[strlen(rresult)-1]='\0';
	}
}

void gccProcess(char *plugdir,char *delwithfilename,char *gccfilename,char *gccofilename)//gcc进程 配置文件目录,当前处理的文件名 gcc第一个参数 gcc第二个参数
{
	char templugdir[2048]={'\0'};
	strncpy(templugdir,plugdir,strlen(plugdir));
	int errorlogfd;
	strncat(templugdir,"compileError.log",strlen("compileError.log"));
	if((errorlogfd=open(templugdir,O_RDWR|O_CREAT|O_APPEND,0666))==-1)
	{
		perror("函数gccProcess--创建compileError.log错误");
	}
	char temperror[512]={'\0'};
	strncpy(temperror,"\n\n【",strlen("\n\n【"));
	strncat(temperror,delwithfilename,strlen(delwithfilename));
	strncat(temperror,"】 报错信息(若为空则无错):\n",strlen("】 报错信息(若为空则无错):\n"));
	if((write(errorlogfd,temperror,strlen(temperror)))==-1)
	{
		perror("函数gccProcess--写入数据时出错！\n");
	}
	dup2(errorlogfd,2);
	execlp("gcc","gcc",gccfilename,"-o",gccofilename,NULL);

}
void gccfail(char *iniDir,char *filename)//编译未通过
{
	char tempopen[2048]={'\0'};
	strncpy(tempopen,iniDir,strlen(iniDir));
	strncat(tempopen,"result.log",strlen("result.log"));
	char tempin[2048]={'\0'};
	strncpy(tempin,"【",strlen("【"));
	strncat(tempin,filename,strlen(filename));
	strncat(tempin,"】----------语法错误----------编译失败----------得分为0\n",strlen("】----------语法错误----------编译失败----------得分为0\n"));
	writeResult(tempopen,tempin);
}
void delwithloop(char *iniDir,char *filename)
{
	char tempopen[2048]={'\0'};
	strncpy(tempopen,iniDir,strlen(iniDir));
	strncat(tempopen,"result.log",strlen("result.log"));
	char tempin[2048]={'\0'};
	strncpy(tempin,"【",strlen("【"));
	strncat(tempin,filename,strlen(filename));
	strncat(tempin,"】==========语法正确----------无限循环----------得分为0\n",strlen("】==========语法正确----------无限循环----------得分为0\n"));
	writeResult(tempopen,tempin);

	loopflag=0;
}
void passfail(char *iniDir,char *filename)//答案不一
{
	char tempopen[2048]={'\0'};
	strncpy(tempopen,iniDir,strlen(iniDir));
	strncat(tempopen,"result.log",strlen("result.log"));
	char tempin[2048]={'\0'};
	strncpy(tempin,"【",strlen("【"));
	strncat(tempin,filename,strlen(filename));
	strncat(tempin,"】==========语法正确----------结果错误----------得分为0\n",strlen("】==========语法正确----------结果错误----------得分为0\n"));
	writeResult(tempopen,tempin);
}
void passcorrect(char *iniDir,char *filename)//答案统一
{
	char tempopen[2048]={'\0'};
	strncpy(tempopen,iniDir,strlen(iniDir));
	strncat(tempopen,"result.log",strlen("result.log"));
	char tempin[2048]={'\0'};
	strncpy(tempin,"【",strlen("【"));
	strncat(tempin,filename,strlen(filename));
	strncat(tempin,"】==========语法正确==========结果正确==========得分为100\n",strlen("】==========语法正确==========结果正确==========得分为100\n"));
	writeResult(tempopen,tempin);
}
void writeSome(char *filename,char * buf)//写文件函数
{
	int fd;

	if((fd=open(filename,O_RDWR|O_CREAT|O_TRUNC,0666))==-1)
	{
		perror("函数writeSome--文件打开失败！\n");
	}
	if((write(fd,buf,strlen(buf)))==-1)
	{
		perror("函数writeSome--写入数据时出错！\n");
	}

	if((close(fd))==-1)
	{
		perror("函数writeSome--文件关闭失败！\n");
	}
	//return fd;
}
int openfileRD(char *filename)//只读方式打开某一文件，返回其文件描述符
{
	int fd;
	if((fd=open(filename,O_RDONLY))==-1)
	{
		perror("函数openfileRD--文件打开失败！");
		return -1;
	}
	return fd;
}
void createonDir(char *path,char *dirname,char *returndir)//在给定的目录下创建目录 returndir带有/
{
	
		char temp[2048]={'\0'};
		strncpy(temp,path,strlen(path));
		strncat(temp,dirname,strlen(dirname));
		if(opendir(temp)==NULL)
		{
			mkdir(temp,0777);
		}
		adddirargv(temp,returndir);
}
void adddirargv(char *beforepath,char *afterpath)//对目录进行添加/操作
{
		if(beforepath[strlen(beforepath)-1]!='/')
		{
			strncat(beforepath,"/",strlen("/"));
		}
		strncpy(afterpath,beforepath,strlen(beforepath));
}
void runProcess(char *plugdir,char *runfilepath,char **exec)
{
	loopflag=0;
	exec[0]=runfilepath;
	execvp(runfilepath,exec);
}

void writeResult(char *filename,char *writesome )//写内容
{
	int fd;
	if((fd=open(filename,O_RDWR|O_CREAT|O_APPEND,0666))==-1)
	{
		perror("函数writeResult--文件打开失败！");
	}
	if((write(fd,writesome,strlen(writesome)))==-1)
	{
		perror("函数writeResult--文件写入失败！");
	}
	if((close(fd))==-1)
	{
		perror("函数writeResult--文件关闭失败！");
	}
}
void showinf(char *dir,char *file1,char *file2)
{
	
	
	char temp1[2048]={'\0'};
	char temp2[2048]={'\0'};
	strncpy(temp1,dir,strlen(dir));
	strncpy(temp2,dir,strlen(dir));
	strncat(temp1,file1,strlen(file1));
	strncat(temp2,file2,strlen(file2));
	char *showexe[2048]={'\0'};
	showexe[0]="gedit";
	showexe[1]=temp1;
	showexe[2]=temp2;
	int sow;
	sow=fork();
	if(sow==0)
	{
		close(2);//关闭标准错误输出
		execvp("gedit",showexe);

	}
	wait(NULL);

}
void cpfailf(char *cprdir,char *cpddir,char *filename)
{
	int cpid;	
	cpid=fork();
	if(cpid==0)
	{
		char temp[2048]={'\0'};
		strncpy(temp,cprdir,strlen(cprdir));
		strncat(temp,filename,strlen(filename));
		execlp("cp","cp",temp,cpddir,NULL);
	}
	wait(NULL);
}
void rmcachedir(char * dir)
{
	int cpid;	
	cpid=fork();
	if(cpid==0)
	{
		execlp("rm","rm","-r",dir,NULL);
	}
	wait(NULL);
}